namespace SharpTalk.Client.WpfApplication.ViewModels
{
    using System;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Threading;
    using Core;
    using Core.Enums;
    using Core.Extensions;
    using Properties;
    using Resources;
    using Services;
    using Views;
    using System.Diagnostics;

    public sealed class MainViewModel
        : ViewModelBase<IMainView>
    {
        private readonly ICommand _exitCommand = null;
        private readonly ICommand _loginCommand = null;
        private readonly ICommand _logoutCommand = null;
        private readonly ICommand _settingsCommand = null;
        private readonly ICommand _aboutCommand = null;
        private readonly ICommand _changeStatusCommand = null;
        private readonly ICommand _chatroomCreateCommand = null;
        private readonly ICommand _chatroomJoinCommand = null;
        private readonly ICommand _chatroomLeaveCommand = null;
        private readonly ICommand _editUserCommand = null;
        private readonly ICommand _chatroomEditCommand = null;
        private readonly ICommand _chatroomSearchCommand = null;

        private bool _checkSpelling = true;

        /// <summary>
        /// Creates a new instance
        /// </summary>
        /// <param name="view"></param>
        public MainViewModel(IMainView view)
            : base(view)
        {
            View.SetIcon(Helpers.LoadImage("talk_32.png"));
            View.Title = App.ApplicationName;

            ApplicationServices.Current.UI = Dispatcher.CurrentDispatcher;
            ApplicationServices.Current.PropertyChanged += (s, e) =>
                                                                {
                                                                    NotifyPropertyChange(() => IsReady);
                                                                    NotifyPropertyChange(() => IsInitializing);
                                                                    NotifyPropertyChange(() => HasUser);
                                                                    NotifyPropertyChange(() => CurrentUser);
                                                                    NotifyPropertyChange(() => IsOnline);
                                                                    NotifyPropertyChange(() => CanUpdateUserSettings);
                                                                };

        }

        public ICommand ChatRoomCreateCommand
        {
            get { return _chatroomCreateCommand ?? new BindableCommand<object>(OnChatRoomCreate); }
        }
        public ICommand ChatRoomSearchCommand
        {
            get { return _chatroomSearchCommand ?? new BindableCommand<object>(OnChatRoomSearch); }
        }
        public ICommand ChatRoomJoinCommand
        {
            get { return _chatroomJoinCommand ?? new BindableCommand<ChatRoomDetails>(OnChatRoomJoin); }
        }
        public ICommand ChatRoomLeaveCommand
        {
            get { return _chatroomLeaveCommand ?? new BindableCommand<ChatRoomDetails>(OnChatRoomLeave); }
        }
        public ICommand ChatRoomEditCommand
        {
            get { return _chatroomEditCommand ?? new BindableCommand<ChatRoomDetails>(OnChatRoomEdit); }
        }
        public ICommand ExitCommand
        {
            get { return _exitCommand ?? new BindableCommand<object>(OnExit); }
        }
        public ICommand LoginCommand
        {
            get { return _loginCommand ?? new BindableCommand<object>(OnLogin); }
        }
        public ICommand LogoutCommand
        {
            get { return _logoutCommand ?? new BindableCommand<object>(OnLogout); }
        }
        public ICommand ChangeStatusCommand
        {
            get { return _changeStatusCommand ?? new BindableCommand<User>(OnChangeStatus); }
        }
        public ICommand SettingsCommand
        {
            get { return _settingsCommand ?? new BindableCommand<object>(OnSettings); }
        }
        public ICommand AboutCommand
        {
            get { return _aboutCommand ?? new BindableCommand<object>(OnAbout); }
        }
        public ICommand EditUserCommand
        {
            get { return _editUserCommand ?? new BindableCommand<object>(OnEditUser); }
        }

        /// <summary>
        /// Obtains the About menu label
        /// </summary>
        public string About
        {
            get
            {
                return string.Format(CultureInfo.CurrentUICulture, Resources.Messages.label_menu_about,
                                     App.ApplicationName);
            }
        }
        /// <summary>
        /// Obtains the current User
        /// </summary>
        public User CurrentUser
        {
            get { return ApplicationServices.Current.CurrentUser; }
        }
        /// <summary>
        /// Determines if the application service is ready
        /// </summary>
        public bool IsReady
        {
            get { return ApplicationServices.Current.IsInitialized; }
        }
        /// <summary>
        /// Determines if the applicationservice is initializing
        /// </summary>
        public bool IsInitializing
        {
            get { return !IsReady; }
        }
        /// <summary>
        /// Determines if there is a current user
        /// </summary>
        public bool HasUser
        {
            get { return ApplicationServices.Current.CurrentUser != null; }
        }
        /// <summary>
        /// Determines if the Current user status is Online
        /// </summary>
        public bool IsOnline
        {
            get { return HasUser && CurrentUser.Status == UserStatusEnum.Online; }
        }
        /// <summary>
        /// Obtains the current user recent chatrooms
        /// </summary>
        public ObservableCollection<ChatRoomDetails> CreatedChatRooms
        {
            get { return ApplicationServices.Current.CreatedChatRooms; }
        }
        /// <summary>
        /// Obtains the Current User contacts list
        /// </summary>
        public ObservableCollection<User> Contacts
        {
            get { return ApplicationServices.Current.Contacts; }
        }
        /// <summary>
        /// Obtains the Discussion collection
        /// </summary>
        public ObservableCollection<Discussion> Discussions
        {
            get { return ApplicationServices.Current.Discussions; }
        }
        /// <summary>
        /// Determines if the Current user has any contacts
        /// </summary>
        public bool HasContacts
        {
            get { return Contacts.Any(); }
        }
        /// <summary>
        /// 
        /// </summary>
        public ApplicationServiceStates ApplicationStates
        {
            get { return ApplicationServices.Current.States; }
        }
        /// <summary>
        /// Obtains or defines if check spelling is active
        /// </summary>
        public bool CheckSpelling
        {
            get { return _checkSpelling; }
            set { _checkSpelling = value; NotifyPropertyChange(() => CheckSpelling); }
        }
        /// <summary>
        /// Determines if a communication is already running with the server for a user update
        /// </summary>
        public bool CanUpdateUserSettings
        {
            get { return HasUser && !CurrentUser.IsUpdating; }
        }

        private void OnEditUser(object value)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            var vm = new UserViewModel(new UserView() { Title = Resources.Messages.label_user_details })
                         {
                             CurrentUser = ApplicationServices.Current.CurrentUser.Clone() as User
                         };
            var result = vm.View.ShowDialog((WindowBase)this.View);
            if (!result.HasValue || !result.Value) return;

            if (!vm.CurrentUser.IsValid())
            {
                DisplayMessageService.ShowMessage((WindowBase)View, Resources.Messages.error_user_details_invalid, true, false);
                return;
            }

            if (HasUser && vm.CurrentUser.Guid != CurrentUser.Guid)
            {
                // New user
                var confirm = DisplayMessageService.ShowDialogMessage((WindowBase)View, Resources.Messages.label_confirmation, Resources.Messages.message_delete_user, false, false, true);
                if (!confirm.HasValue || !confirm.Value) return;

                // Leave all chatrooms if any
                var roomsToLeave = Discussions.Select(d => d.Chatroom).ToArray();
                foreach (var room in roomsToLeave)
                    ApplicationServices.Current.LeaveChatRoom(room);

                // register the new one (deletes the old one)
                Task.Factory.StartNew(() =>
                                          {
                                              if (ApplicationServices.Current.RegisterNewUser(vm.CurrentUser))
                                                  OnLogin(null);
                                          });
            }
            else
            {
                // Update Current user
                ApplicationServices.Current.CurrentUser.IsNotifying = false;
                ApplicationServices.Current.CurrentUser.FirstName = vm.CurrentUser.FirstName;
                ApplicationServices.Current.CurrentUser.LastName = vm.CurrentUser.LastName;
                ApplicationServices.Current.CurrentUser.NickName = vm.CurrentUser.NickName;
                ApplicationServices.Current.CurrentUser.Image = vm.CurrentUser.Image;
                ApplicationServices.Current.CurrentUser.IsNotifying = true;
                ApplicationServices.Current.CurrentUser.Refresh();
            }
        }

        private void OnExit(object value)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            View.Close();
        }

        private void OnLogin(object value)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            if (ApplicationServices.Current.CurrentUser != null)
            {
                ApplicationServices.Current.CurrentUser.Status = UserStatusEnum.Online;
            }
            else
            {
                var vm = new UserViewModel(new UserView() { Title = Resources.Messages.label_new });
                var result = vm.View.ShowDialog((WindowBase)this.View);
                if (result.HasValue && result.Value)
                {
                    if (!vm.CurrentUser.IsValid())
                        DisplayMessageService.ShowMessage((WindowBase)View, Resources.Messages.error_user_details_invalid, true, false);
                    else
                        Task.Factory.StartNew(() =>
                                                  {
                                                      if (ApplicationServices.Current.RegisterNewUser(vm.CurrentUser))
                                                          OnLogin(null);
                                                  });
                }
            }
        }

        private void OnLogout(object value)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());

            var roomsToLeave = Discussions.Select(d => d.Chatroom).ToArray();
            foreach (var room in roomsToLeave)
                ApplicationServices.Current.LeaveChatRoom(room);

            if (ApplicationServices.Current.CurrentUser != null)
                ApplicationServices.Current.CurrentUser.Status = UserStatusEnum.Offline;
        }

        private void OnChangeStatus(User user)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            if (user != null && user.IsUpdating) return;

            if (user == null)
            {
                // Creates a new User
                OnLogin(null);
                return;
            }

            if (user.Status == UserStatusEnum.Online)
                OnLogout(null);
            else
                OnLogin(null);
        }

        private void OnSettings(object value)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            var vm = new SettingsViewModel(new SettingsView() { Title = Resources.Messages.label_settings });
            var result = vm.View.ShowDialog((WindowBase)View);
            if (result.HasValue && result.Value)
            {
                Settings.Default.Save();

                ApplicationServices.Current.SaveTruncatedMessages = Settings.Default.SaveMessages;
                ApplicationServices.Current.UpdateMaximumDiscussionMessageCount(Settings.Default.MaximumMessages);

                SourceLevels level;
                if (Enum.TryParse(vm.SelectedTraceLevel, true, out level))
                    App.Trace.SetSourceLevel(level);

                if (!Equals(CultureInfo.CurrentUICulture, vm.SelectedCulture))
                    try
                    {
                        Application.Current.Dispatcher.Thread.CurrentUICulture = vm.SelectedCulture;
                        Messages.Culture = vm.SelectedCulture;
                    }
                    catch (Exception exp)
                    {
                        DisplayMessageService.ShowMessage((WindowBase)View, exp.Message, true, false);
                    }

                if (ApplicationServices.Current.CurrentUser != null && !string.Equals(ApplicationServices.Current.CurrentUser.Uri, Settings.Default.LocalServiceUri, StringComparison.InvariantCulture))
                    ApplicationServices.Current.CurrentUser.Uri = Settings.Default.LocalServiceUri;
            }
            else
                Settings.Default.Reload();
        }

        private void OnAbout(object value)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            // Show about window
        }

        private void OnChatRoomEdit(ChatRoomDetails chatroom)
        {
            var clone = (ChatRoomDetails)chatroom.Clone();

            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            var vm = new ChatRoomDetailsViewModel(new ChatRoomDetailsView() { Title = Messages.label_chatroom_edit })
                         {
                             ChatRoomDetails = clone,
                             IsNew = false
                         };

            var result = vm.View.ShowDialog((WindowBase)this.View);
            if (!result.HasValue || !result.Value) return;

            byte[] password = null;
            bool definePassword = vm.PasswordDefined;

            if (definePassword)
                password = vm.PasswordHash;

            Task.Factory.StartNew(() => ApplicationServices.Current.UpdateChatRoomDetails(clone, definePassword, password));
        }

        private void OnChatRoomCreate(object value)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            var vm = new ChatRoomDetailsViewModel(new ChatRoomDetailsView() { Title = Messages.label_new })
                         {
                             ChatRoomDetails = value as ChatRoomDetails,
                             IsNew = true
                         };

            var result = vm.View.ShowDialog((WindowBase)this.View);
            if (!result.HasValue || !result.Value) return;

            var chatroom = vm.ChatRoomDetails;
            byte[] password = null;
            if (vm.HasPassword)
            {
                password = vm.PasswordHash;
                if (!chatroom.Type.HasFlag(ChatRoomTypeEnum.Protected))
                    chatroom.Type |= ChatRoomTypeEnum.Protected;
            }
            Task.Factory.StartNew(() => ApplicationServices.Current.CreateNewChatRoom(chatroom, password));
        }

        private void OnChatRoomSearch(object value)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            if (!HasUser) return;

            var vm = new ChatRoomSearchViewModel(new ChatRoomSearchView());
            var result = vm.View.ShowDialog((WindowBase)this.View);
            if (!result.HasValue || !result.Value || vm.SelectedChatRoom == null) return;

            var room = vm.SelectedChatRoom;
            OnChatRoomJoin(room);
        }

        private void OnChatRoomJoin(ChatRoomDetails chatroom)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            if (!HasUser) return;

            if (CurrentUser.Status != UserStatusEnum.Online)
            {
                DisplayMessageService.ShowMessage((WindowBase)this.View, Resources.Messages.error_user_status, true, false);
                return;
            }

            if (chatroom == null)
            {
                var vm = new ChatRoomJoinViewModel(new ChatRoomJoinView());
                var result = vm.View.ShowDialog((WindowBase)View);
                if (!result.HasValue || !result.Value) return;

                var pass = vm.PasswordHash;
                var name = vm.ChatRoomName;

                Task.Factory.StartNew(() => ApplicationServices.Current.FindChatRoomAndJoin(name, ChatRoomTypeEnum.NotDefined, pass));
            }
            else
            {
                byte[] password = null;

                if (!chatroom.AlreadyJoined() &&
                    chatroom.Type.HasFlag(ChatRoomTypeEnum.Protected))
                {
                    var vm = new PasswordViewModel(new PasswordView());
                    var result = vm.View.ShowDialog((WindowBase)this.View);
                    if (result.HasValue && result.Value)
                    {
                        password = vm.PasswordHash;
                    }
                    else
                        return;
                }

                Task.Factory.StartNew(() => ApplicationServices.Current.JoinChatRoom(chatroom, password));
            }

        }

        private void OnChatRoomLeave(ChatRoomDetails chatroom)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            if (chatroom == null) return;
            Task.Factory.StartNew(() => ApplicationServices.Current.LeaveChatRoom(chatroom));
        }
    }
}